﻿namespace SharpF2.Model
{
	using System;
	using System.Collections.Generic;
	using System.Linq;
	using System.Text;
	using System.Text.RegularExpressions;

	public class PatternGenerator
	{
		#region Private Attributes

		private static readonly Char[] allowedChars = new[] {'-', ' ', '.', '_'};

		#endregion

		#region Public Members

		public static String GetFullPattern(String pattern, out String[] usedIdentifiers)
		{
			var matchPattern = new StringBuilder(pattern.Length * 12);
			usedIdentifiers = new string[pattern.Length + 1];
			var identifier = IdentifierEnumerator(null);
			identifier.MoveNext();
			var i = 0;
			for (; i < pattern.Length; ++i, identifier.MoveNext())
			{
				matchPattern.Append(String.Format("(?<{0}>.*){1}", identifier.Current,
				                                  pattern.Substring(i, 1).Replace(".", "\\.")));
				usedIdentifiers[i] = String.Format("${{{0}}}", identifier.Current);
			}
			matchPattern.Append(String.Format("(?<{0}>.*)", identifier.Current));
			usedIdentifiers[i] = String.Format("${{{0}}}", identifier.Current);

			return matchPattern.ToString();
		}

		public static KeyValuePair<String, String> GetPattern(IEnumerable<String> names, IEnumerable<String> usedIdentifiers)
		{
			var x = new List<KeyValuePair<String, String>>();
			var minl = Int32.MaxValue;
			var mint = String.Empty;

			foreach (var name in names)
			{
				var f = Filter(name);
				if (f.Length < minl)
				{
					minl = f.Length;
					mint = f;
				}
				x.Add(new KeyValuePair<String, String>(f, name));
			}

			var goodPattern = String.Empty;
			foreach (var pattern in PatternEnumerator(mint))
			{
				var faild = true;
				foreach (var item in x)
				{
					if (false == (faild = Test(item.Key, pattern)))
						break;
				}

				if (false == faild) continue;

				goodPattern = pattern;
				break;
			}


			if (String.IsNullOrEmpty(goodPattern))
			{
				var availableIdentifier = IdentifierEnumerator(usedIdentifiers);
				availableIdentifier.MoveNext();

				var inputPattern = String.Format("(?<{0}>.*)", availableIdentifier.Current);
				var outputPattern = String.Format("${{{0}}}", availableIdentifier.Current);
				return new KeyValuePair<String, String>(inputPattern, outputPattern);
			}

			// Search for aggregates
			String[] identifiers;
			var fullPattern = GetFullPattern(goodPattern, out identifiers);
			var regex = new Regex(fullPattern);

			var counters = new Int32[identifiers.Length];
			for (var i = 0; i < identifiers.Length; i++)
				counters[i] = 0;

			foreach (var name in names)
			{
				var match = regex.Match(name);
				if (!match.Success) continue;
				for (var i = 0; i < counters.Length; ++i)
					if (!String.IsNullOrEmpty(match.Result(identifiers[i])))
						++counters[i];
			}

			var matchPattern = new StringBuilder(goodPattern.Length * 12);
			var combinePattern = new StringBuilder(goodPattern.Length + 12);
			var identifier = IdentifierEnumerator(usedIdentifiers);
			var index = 0;
			foreach (var i in counters)
			{
				identifier.MoveNext();

				if (goodPattern.Length <= index)
				{
					matchPattern.Append(String.Format("(?<{0}>.*)", identifier.Current));
					combinePattern.Append(String.Format("${{{0}}}", identifier.Current));
					break;
				}

				if (0 == i)
				{
					matchPattern.Append(goodPattern.Substring(index, 1).Replace(".", "\\."));
					combinePattern.Append(goodPattern.Substring(index, 1).Replace(".", "\\."));
				}
				else
				{
					matchPattern.Append(String.Format("(?<{0}>.*){1}", identifier.Current,
					                                  goodPattern.Substring(index, 1).Replace(".", "\\.")));
					combinePattern.Append(String.Format("${{{0}}}{1}", identifier.Current,
					                                    goodPattern.Substring(index, 1).Replace(".", "\\.")));
				}
				++index;
			}

			return new KeyValuePair<String, String>(matchPattern.ToString(), combinePattern.ToString());
		}

		#endregion

		#region Private Members

		private static String Filter(IEnumerable<char> input)
		{
			return new string(input.Where(c => allowedChars.Contains(c)).ToArray());
		}

		private static IEnumerator<String> IdentifierEnumerator(IEnumerable<String> usedIdentifiers)
		{
			var chars = "abcdefghijklmnopqrstuvwxyz";
			for (var i = 1; i < 8; ++i)
			{
				var id = new Char[i];
				var idIndexes = new Byte[i];
				for (var k = 0; k < i; ++k)
					idIndexes[k] = 0;

				var a = i - 1;
				while (a >= 0 && a < i && idIndexes[a] < chars.Length)
				{
					for (var c = 0; c < i; ++c)
						id[c] = chars[idIndexes[c]];

					var stringId = new String(id);
					if (null == usedIdentifiers || !usedIdentifiers.Contains(stringId))
						yield return stringId;

					if (idIndexes[a] == chars.Length - 1)
					{
						idIndexes[a] = 0;
						++a;
					}
					else
						idIndexes[a]++;
				}
			}
		}

		private static IEnumerable<String> PatternEnumerator(String pattern)
		{
			if (String.IsNullOrEmpty(pattern)) yield break;

			yield return pattern;

			var remove = 1;

			for (; remove < pattern.Length; ++remove)
				for (var index = 0; (index + remove) <= pattern.Length; ++index)
					yield return pattern.Remove(index, remove);
		}

		private static bool Test(String text, String pattern)
		{
			var textArray = text.ToCharArray();
			var patternArray = pattern.ToCharArray();
			var i = 0;
			var j = 0;
			for (; i < textArray.Length && j < patternArray.Length; ++i)
				if (textArray[i].Equals(patternArray[j]))
					++j;

			return j >= patternArray.Length;
		}

		#endregion
	}
}